/*

   funcion que controla los totales de iva de comprobantes - ale 05/05/2021

*/

CREATE OR REPLACE FUNCTION public.com_verificartotalesxpiva(
    idcomtotiva integer,
    character varying)
  RETURNS character varying AS
$BODY$
-- Recibe como parametros:
-- 1) el idcomtotiva de las reglas de desvios de IVA a aplicar
-- 2) un string de la sig. forma:  tasa, netogravado, montoiva; tasa, netogravado, montoiva; ....; 
-- Nota: netogravado y montoiva son siempre positivos
-- Retorna un string: tasa, montoivacalculado, errornivel, errormensaje, erroridevento; tasa, montoivacalculado, errornivel, errormensaje, erroridevento; ....;
-- Niveles de error:
--  100: solo lo maneja la funcion. Son los errores que luego AFIP controla en CITI-Compras, CITI-ventas.
--       La app NO permite grabar un comm cuyos totales de IVA arrojen esta categoria de errores.
--  (50, 100): errores definibles en comtotivadet. La app NO permite grabar un comm cuyos totales de IVA
--             arrojen esta categoria de errores. 
--  (0,50]: advertencias definibles en comtotivadet. Se puede asociar un evento comtotivadet.idambacn y
--          controlar el error con ese evento. Si no se asocia evento, la app permite grabar un comm
--          cuyos totales de IVA arrojen este error. 
--  0: Sin error
-- -1: Se ha definido No controlar totales de IVA
--
-- para probar SELECT com_verificartotalesxpiva(NULL, '10.5,0,0;21,84.25,17.69;27,0,0;')  AS totalesXPIVA    (sin errores)
--             SELECT com_verificartotalesxpiva(NULL, '10.5,0,0;21,84.25,16;27,0,0;')  AS totalesXPIVA       (desvio IVA = 9.5662)
--
DECLARE
pIdComTotIva ALIAS FOR $1;
pCadenaIVAs  ALIAS FOR $2;
vCabeza varchar;
vCola varchar;
vPos int4;
vRta varchar;
vTasa numeric(16, 2);
vNetoGravado numeric(16, 2);
vMontoIVA numeric(16, 2);
vMontoIVACalculado numeric(16, 2); 
vDesvioIVAPesos numeric(16, 2);
vdesvioIVAPorc  numeric(16, 4);
vCursorAbierto varchar;
vEsNoControlar varchar;

-- valores de la rta
vErrNivel int4;
vErrMsg varchar;
vErrIdEv  int4;
-- cursor 
reg_desviopesos comtotivadet.desviopesos%TYPE;
reg_desvioporc  comtotivadet.desvioporc%TYPE;
reg_nivelerror  comtotivadet.nivelerror%TYPE;
reg_idambacn    comtotivadet.idambacn%TYPE;
cursor_comtotivadet refcursor;
BEGIN
vEsNoControlar := NULL;
vCursorAbierto := 'N';
vRta := '';
vCola := TRIM(pCadenaIVAs);
WHILE (LENGTH(vCola) > 0) LOOP
-- Parsear cadenaIVAs para cortar cada grupo (tasa, netogravado, montoiva).
  vPos := strpos(vCola, ';');
  IF (vPos = 0) THEN
    vCabeza := vCola;
    vCola   := '';
  ELSE
    vCabeza := trim(substr(vCola, 1, vPos-1));
    vCola   := trim(substr(vCola, vPos+1, length(vCola)));
  END IF;

  vPos := strpos(vCabeza, ',');

  EXIT WHEN vPos = 0; -- si no encuentra el separador sale del while

-- Separa tasa, netrogravado y montoIVA en variables.
  SELECT split_part(vCabeza, ',', 1) ::NUMERIC(16,2),
         split_part(vCabeza, ',', 2) ::NUMERIC(16,2),
         split_part(vCabeza, ',', 3) ::NUMERIC(16,2) INTO vTasa, vNetoGravado, vMontoIVA;

  vErrNivel := NULL;
  vErrMsg   := '';
  vErrIdEv  := -1;
  vMontoIVACalculado := NULL;
  vEsNoControlar := NULL;
  
-- verifica los valores para la tasa de iva
  IF (vNetoGravado <> 0.00) OR (vMontoIVA <> 0.00) THEN
-- ======== Errores que controla AFIP =========
-- AFIP caso 1: hay Neto gravado y falta IVA
    IF (vNetoGravado <> 0.00) AND (vMontoIVA = 0.00) THEN
      vErrNivel := 100;
      vErrMsg   := 'El IVA no puede ser cero.';
    END IF;

-- AFIP caso 2: Neto gravado < IVA (se comparan valores sin signo)
    IF (vErrNivel IS NULL) AND (vNetoGravado < vMontoIVA) THEN
       vErrNivel := 100;
       vErrMsg   := 'El IVA no puede ser mayor que el Neto gravado.';
    END IF;  

-- ======== errores por desvio del IVA =========
-- caso 3: ver si (Neto gravado * tasa) y MontoIVA cumplen los desvios permitidos
--  03/05/2021: se implemento caso default (cuando idcomtotiva es NULL, o sea no hay definido caso para el tipocom)
--  CASO DEFAULT:  desvioporc = 5.00 = nivel 50 (aviso); desvioporc = 10.00 = nivel 50 (error)

 --   IF (vErrNivel IS NULL) AND (pidcomtotiva IS NOT NULL) THEN -- pidcomtotiva = NULL es caso default
    IF (vErrNivel IS NULL) THEN
    -- calcular desvios del monto de IVA a controlar
       vMontoIVACalculado := abs(vNetoGravado * vTasa / 100);
       vdesvioIVAPesos := abs(vMontoIVACalculado - vMontoIVA);  
       vdesvioIVAPorc  := abs( 100.00 - (vMontoIVA*10000/(vNetoGravado * vTasa)));  

/*     raise NOTICE '   vMontoIVA=%',vMontoIVA;
     raise NOTICE '   vNetoGravado=%',vNetoGravado;
     raise NOTICE '   vTasa=%',vTasa;
     raise NOTICE '   vMontoIVACalculado=%',vMontoIVACalculado;
     raise NOTICE '   vdesvioIVAPesos=%',vdesvioIVAPesos;
     raise NOTICE '   vdesvioIVAPorc=%',vdesvioIVAPorc;
*/          
     -- abrir cursor si esta cerrado
     IF (vCursorAbierto = 'N') THEN
         OPEN cursor_comtotivadet SCROLL   
           FOR (SELECT desviopesos, desvioporc, nivelerror, idambacn FROM comtotivadet WHERE comtotivadet.idcomtotiva = pidcomtotiva
                UNION 
                -- default warning: desvio 5% 
                SELECT CAST(NULL AS NUMERIC(16,2)) AS desviopesos, 5.00 AS desvioporc, 50 AS nivelerror, NULL AS idambacn WHERE NOT EXISTS (SELECT 0 FROM comtotivadet WHERE comtotivadet.idcomtotiva = pidcomtotiva)
                -- default error: desvio 10%
                UNION
                SELECT CAST(NULL AS NUMERIC(16,2)) AS desviopesos, 10.00 AS desvioporc, 99 AS nivelerror, NULL AS idambacn WHERE NOT EXISTS (SELECT 0 FROM comtotivadet WHERE comtotivadet.idcomtotiva = pidcomtotiva)                  
                ORDER BY nivelerror DESC) ; 
         vCursorAbierto := 'S';
      END IF; 
      
      FETCH FIRST FROM cursor_comtotivadet INTO reg_desviopesos, reg_desvioporc, reg_nivelerror, reg_idambacn;

      LOOP EXIT WHEN (NOT FOUND) OR (vErrNivel IS NOT NULL);
    -- procesar cada una de las filas: el desvio debe cumplir comtotaivadet.desviopesos OR comtotaivadet.desvioporc
    -- Si ambos desvios son NULL -> no se controla
        IF NOT ( ((reg_desvioPesos IS NULL) OR (vdesvioIVAPesos <= reg_desvioPesos)) AND
                 ((reg_desvioPorc IS NULL) OR (vdesvioIVAPorc <= reg_desvioPorc)) ) THEN
           vErrNivel := reg_nivelerror;
           vErrIdEv  := reg_idambacn;
           vEsNoControlar := 'N';
            --verificar si hay desvio en pesos       
           IF (reg_desvioPesos IS NOT NULL) AND (vdesvioIVAPesos > reg_desvioPesos) THEN
             vErrMsg := 'La diferencia en el IVA (' || vdesvioIVAPesos || ' $) supera el importe permitido (' || reg_desvioPesos || ' $).';
           ELSE
            --verificar desvio en porcentaje
             vErrMsg := 'La diferencia en el IVA (' || vdesvioIVAPorc || ' %) supera el porcentaje permitido (' || reg_desvioPorc || ' %).';
           END IF; 
        ELSIF (reg_desvioPesos IS NULL) AND (reg_desvioPorc IS NULL) AND vEsNoControlar IS NULL THEN
          vEsNoControlar := 'S'; -- verificar el caso elegido es 'No controlar IVA' (caso con ambos desvios en NULL)
        END IF;         

        FETCH NEXT FROM cursor_comtotivadet INTO reg_desviopesos, reg_desvioporc, reg_nivelerror, reg_idambacn;

      END LOOP;

    END IF;

  END IF;
  --Si no hubo errores, verificar si se trata del caso 'No controlar Totales de IVA'
  IF (vErrNivel IS NULL) AND (vEsNoControlar = 'S') THEN
    vErrNivel := -1;
    vErrMsg := 'No controlado';
  END IF;

  -- armar cadena de respuesta

  -- ATENCION: para versiones anteriores a 456.00  va esta vRta (reemplazar las lineas) --------------
  --   vRta := vRta || (vTasa)::varchar || ','
  --                || COALESCE(vErrNivel, 0)::varchar || ',' || COALESCE(vErrMsg, '') || ',' || COALESCE(vErrIdEv, -1)::varchar || ';';
  -- -------------------------------------------------------------------------------------------------

  vRta := vRta || (vTasa)::varchar || ',' || COALESCE(vMontoIVACalculado, '0')::varchar || ',' 
               || COALESCE(vErrNivel, 0)::varchar || ',' || COALESCE(vErrMsg, '') || ',' || COALESCE(vErrIdEv, -1)::varchar || ';';
  
END LOOP;

RETURN vRta;

END;$BODY$
  LANGUAGE plpgsql VOLATILE COST 100;
ALTER FUNCTION public.com_verificartotalesxpiva(integer, character varying) OWNER TO abacouser;


---------------------------------------------
-- Agregar caso "No controlar Totales de IVA"
---------------------------------------------

INSERT INTO comtotiva(id, nombre) VALUES ((SELECT nextval('seq_comtotiva')), 'No controlar Totales de IVA');
INSERT INTO comtotivadet (idcomtotiva, id, desviopesos, desvioporc, nivelerror, idambacn) 
                  VALUES ((SELECT MAX(id) FROM comtotiva), (SELECT nextval('seq_comtotivadet')) , NULL, NULL, 1, NULL);


-- ====================================
 SELECT aaVersion('456.00','455.00');
-- ====================================